#include "LinuxSerialPort.h"
#include <cstdio>         //标准输入输出定义
#include <cstdlib>        //标准函数库定义
#include <cstring>
#include <cerrno>
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "termios.h"
#include "errno.h"
#include <iostream>

using namespace std;
using namespace MC500;

speed_t LinuxSerialPort::GetUARTSpeedByValue(uint32_t baudrate) {
    switch (baudrate) {
    case 50:
        return B50;
    case 75:
        return B75;
    case 134:
        return B134;
    case 150:
        return B150;
    case 200:
        return B200;
    case 300:
        return B300;
    case 600:
        return B600;
    case 1200:
        return B1200;
    case 1800:
        return B1800;
    case 2400:
        return B2400;
    case 4800:
        return B4800;
    case 9600:
        return B9600;
    case 19200:
        return B19200;
    case 38400:
        return B38400;
    case 57600:
        return B57600;
    case 115200:
        return B115200;
    case 230400:
        return B230400;
    default:
        break;
    }
    return B0;
}

LinuxSerialPort::~LinuxSerialPort()
    {
        if (fd_ != -1)
        {
            close(fd_);
        }
    }
    

ErrorCode LinuxSerialPort::Init(uint32_t speed){
    fd_ = open(std::string(("/dev/") + devName_).c_str(), O_RDWR | O_NOCTTY | O_SYNC);
    if(fd_ == -1){
        LOG("Open dev failed!dev=%s", devName_.c_str());
        return -1;
    }

    struct termios tty;

    if (tcgetattr(fd_, &tty) < 0) {
        LOG("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    speed_t speedUART = GetUARTSpeedByValue(speed);

    if(speedUART == B0) {
        LOG("Please input a supported speed!speed=%d", speed);
        return -1;
    }

    // 设置发送速率
    cfsetospeed(&tty, speedUART);
    // 设置接收速率
    cfsetispeed(&tty, speedUART);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd_, TCSANOW, &tty) != 0) {
        LOG("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

//发送数据
ErrorCode LinuxSerialPort::Send(const uint8_t *data, uint16_t len)
{
    int32_t wlen = write(fd_, data, len);
    if (wlen != len) {
        printf("Error from write: %d, %d\n", wlen, errno);
        return -1;
    }

    tcdrain(fd_);    // 等待发送完成
    return 0;
}

#define RECV_BUFF_SIZE 512
#define EndMark '\r'
//接受数据
ErrorCode LinuxSerialPort::Read(uint8_t *data, uint16_t buffSize, uint16_t &readCount){
    //cout << "call read..." << endl;
    char buff[RECV_BUFF_SIZE] = {0};
    int pos = 0;
    char* endMark = NULL;
    //char* rsp = NULL;
    do {
        int readCount = read(fd_, buff + pos, RECV_BUFF_SIZE - pos);
        if (readCount < 0) {
            printf("Error from read: %d: %s\n", pos, strerror(errno));
            return -1;
        }
        pos += readCount;
        buff[pos] = '\0';
        //char* token = strtok(str, EndMark);
        endMark = strchr(buff, EndMark);
        //cout<< "read " << pos <<"chars.buff=" << buff << ",endMark=" << (endMark == NULL) << endl;
    }while(endMark == NULL);

    uint16_t rspDataLength = endMark - buff;
    if(rspDataLength >= buffSize - 1) {
        cout << __func__ << ":buffer too small!" << endl;
        return -1;
    }
    uint16_t trimCount = 0;
    for(; trimCount < RECV_BUFF_SIZE && trimCount < rspDataLength; trimCount++) {
        if(buff[trimCount] != '\r' && buff[trimCount] != '\n'){
            break;
        }
    }
    memcpy(data, buff + trimCount, rspDataLength - trimCount);
    data[rspDataLength - trimCount] = '\0';
    readCount = rspDataLength - trimCount;
    //cout<< "read: " << data << "size:" << strlen((char*)data) << endl;
    return 0;
}